#include <stdio.h>
#include <stdlib.h>    //free使用的头文件
#include <string.h> //strdup
#include <curl/curl.h> //libcurl的头文件
#include "cJSON.h"

//都没有考虑内存不足的情况，实际需要处理这类异常

//获取令牌
char* get_token(const char* api_key, const char* secret_key)
{


    FILE *fp;

    //以只写方式打开文件
    //fp = fopen("hello.txt", "w");
    //响应消息的地址
    char *response = NULL;
    //响应消息的长度
    size_t resplen = 0;
    //创建内存文件，当通过文件句柄写入数据时，会自动分配内存
    fp = open_memstream(&response, &resplen);
    if (fp == NULL) //打开文件失败，打印错误信息并退出
    {
        perror("open_memstream() failed");
        return NULL;
    }


    //初始化HTTP客户端
    CURL *curl = curl_easy_init();

    if (curl == NULL)
    {
        perror("curl_easy_init() failed");
        return NULL;
    }

    char *uri = NULL;

    //生成拼接uri,asprintf会自动分配内存
    asprintf(&uri, 
            "https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s", 
            api_key,
            secret_key);

    //准备HTTP请求消息，设置API地址（URI）
    curl_easy_setopt(curl, CURLOPT_URL, uri);
    
    //如果不指定写入的文件，libcurl会把服务器响应消息中的内容打印到屏幕上
    //如果指定了文件句柄，libcurl会把服务器响应消息中的内容写入文件
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);

    //发送HTTP请求消息，等待服务器的响应消息
    CURLcode error = curl_easy_perform(curl);
    if (error != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(error));
        curl_easy_cleanup(curl);
        free(uri);
        fclose(fp);
        free(response);
        return NULL;
    }

    //释放HTTP客户端申请的资源
    curl_easy_cleanup(curl);

    //释放读取网址分配asprintf的内存
    free(uri);

    //关闭内存文件
    fclose(fp);

    //打印返回
    //puts(response);

    cJSON* json = cJSON_Parse(response);
    if (json == NULL)
    {
        const char *error_pos = cJSON_GetErrorPtr();
        if (error_pos != NULL)
        {
            fprintf(stderr, "Error before: %s\n", error_pos);
        }
        return NULL;
    }

    //获取JSON报文中的acess_token字段
    cJSON *access_token = cJSON_GetObjectItemCaseSensitive(json, "access_token");

    if (!cJSON_IsString(access_token))
    {
        cJSON *error_description = cJSON_GetObjectItemCaseSensitive(json, "error_description");
        fprintf(stderr, "%s \n", error_description->valuestring);
        return NULL;
    }


    //使用完之后，需要使用free释放token
    char* token = strdup(access_token->valuestring);

    free(response);
    cJSON_Delete(json);

    return token;
}

void text2speech(const char* token, const char* text)
{
    CURL* curl = curl_easy_init();

    //发送到百度的字符串需要进行两次URL编码
    char* temp = curl_easy_escape(curl, text, strlen(text));
    char *data = curl_easy_escape(curl, temp, strlen(temp));
    curl_free(temp);

    //组装post请求发送的数据
    char* postdata;
    asprintf(&postdata, "tex=%s&lan=zh&cuid=dyp&ctp=1&aue=6&tok=%s", data, token);
    curl_free(data);

    //打开准备写入的音频文件
    FILE *fp = popen("aplay -q -", "w");

    if(fp == NULL)
    {
        perror("fopen() failed");
        return; 
    }

    curl_easy_setopt(curl, CURLOPT_URL, "https://tsn.baidu.com/text2audio");
    //配置客户端，使用HTTP的POST方法发送请求消息
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    //配置通过POST发送的数据
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);

    //发送HTTP请求消息，等待服务器的响应消息
    CURLcode error = curl_easy_perform(curl);
    if (error != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(error));
        curl_easy_cleanup(curl);
        fclose(fp);
        return ;
    }


    //释放
    curl_easy_cleanup(curl);
    //关闭音频
    pclose(fp);

    //调用播放器软件播放音频文件
}



//百度语言合成API，一次最多可转换2048个字符
#define LINE_LEN 2048

//保存输入字符串的缓存区
char line[LINE_LEN];

int main()
{
    char* token = get_token("ohZQGov7stmOduZmNp6aGGI2", "Em0SA1AeeATBLs3AffCDgRHXh3vyWNi5");
    if(NULL == token)
    {
        return EXIT_FAILURE;
    }

    //从标准输入读取一行字符
    while(fgets(line, LINE_LEN, stdin) != NULL)
    {
        text2speech(token, line);
    }

    free(token);
    return 0;
}